package com.eastedu.report.principal.assemble;

import com.eastedu.common.enums.DateCodeType;
import com.eastedu.report.common.model.parameter.ReportGenerateParameter;
import com.eastedu.report.common.model.po.CommonDimensionEntity;
import com.eastedu.report.common.model.po.CooperativeTeacherPO;
import com.eastedu.report.common.model.po.ResourceReceiveDetailUsagePO;
import com.eastedu.report.common.model.po.TeacherIdentityResourceUsagePO;
import com.eastedu.report.common.util.NumberFormatUtil;
import com.eastedu.report.common.util.SqlUtil;
import com.eastedu.report.mapper.CooperativeTeacherMapper;
import com.eastedu.report.mapper.ResourceReceiveDetailUsageMapper;
import com.eastedu.report.mapper.TeacherIdentityResourceUsageMapper;
import com.eastedu.report.principal.model.overview.TeacherResourceUtilizationReportData;
import com.eastedu.utils.CollectionUtils;
import com.eastedu.utils.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 整体概览报告数据组装
 *
 * @author superman
 */
@Service
public class OverallOverviewReportDataAssembly implements ReportDataAssembly {

    private final TeacherIdentityResourceUsageMapper teacherIdentityResourceUsageMapper;
    private final ResourceReceiveDetailUsageMapper resourceReceiveDetailUsageMapper;
    private final CooperativeTeacherMapper cooperativeTeacherMapper;

    /**
     * Instantiates a new Overall overview report data assembly.
     *
     * @param teacherIdentityResourceUsageMapper the teacher identity resource usage mapper
     * @param resourceReceiveDetailUsageMapper   the resource receive detail usage mapper
     * @param cooperativeTeacherMapper           the cooperative teacher mapper
     */
    public OverallOverviewReportDataAssembly(TeacherIdentityResourceUsageMapper teacherIdentityResourceUsageMapper, ResourceReceiveDetailUsageMapper resourceReceiveDetailUsageMapper, CooperativeTeacherMapper cooperativeTeacherMapper) {
        this.teacherIdentityResourceUsageMapper = teacherIdentityResourceUsageMapper;
        this.resourceReceiveDetailUsageMapper = resourceReceiveDetailUsageMapper;
        this.cooperativeTeacherMapper = cooperativeTeacherMapper;
    }

    @Override
    public Map<String, Object> assemble(ReportGenerateParameter parameter, List<String> schoolPeriods, List<String> subjects) {
        DateCodeType codeType = parameter.getCodeType();
        Integer dateCode = parameter.getDateCode();
        Integer lastMonthDateCode = DateUtil.getLastMonthDateCode(dateCode);
        List<TeacherIdentityResourceUsagePO> usages = teacherIdentityResourceUsageMapper.selectList(SqlUtil.queryWrapper(parameter,
                                                                                                                         w -> w.and(c ->
                                                                                                                                            c.or(a -> a.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, dateCode))
                                                                                                                                                    .or(a -> a.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, lastMonthDateCode))),
                                                                                                                         TeacherIdentityResourceUsagePO.class));

        List<CooperativeTeacherPO> cooperativeTeachers = cooperativeTeacherMapper.selectList(SqlUtil.queryWrapper(parameter, CooperativeTeacherPO.class));
        List<ResourceReceiveDetailUsagePO> secretExamReceives = resourceReceiveDetailUsageMapper.selectList(SqlUtil.queryWrapper(parameter,
                                                                                                                                 w -> w.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, dateCode)
                                                                                                                                         .eq(ResourceReceiveDetailUsagePO::getTeachingActivity, "同步测验")
                                                                                                                                         .eq(ResourceReceiveDetailUsagePO::getResourceTypeName, "绝密试卷"),
                                                                                                                                 ResourceReceiveDetailUsagePO.class));

        Map<String, Object> map = new HashMap<>(10);

        // 文案输出规则：
        //第1句文案输出：
        //本月，学校共计有xx位学科教师使用了资源，使用资源的学科教师占比为xx.x%，较上月增加（减少）xx.x%;
        //
        //第2句文案输出：
        //1、若当月该学校未合作试卷保密员，则第2句话不输出。
        //2、若当月合作了试卷保密员，且未接收到绝密资料，则文案输出：
        //      本月暂未接收到绝密试卷。
        //3、若当月合作了试卷保密员，且接收到绝密资料，则：
        //  （1）当月合作了1位试卷保密员：
        //          ① 试卷保密员使用了资源，则文案输出：
        //                试卷保密员使用了资源。
        //             ② 试卷保密员未使用资源，则文案输出：
        //                 试卷保密员未使用资源。
        //
        //  （2）若当月该学校合作了2位及以上的试卷保密员，则：
        //          ① 试卷保密员使用了资源，则文案输出：
        //        xx位试卷保密员使用了资源，使用资源的试卷保密员占比为xx%，较上月增加（减少）xx.x%。
        //            ② 试卷保密员未使用资源，则文案输出：
        //        试卷保密员未使用资源，使用资源的试卷保密员占比较上月减少xx.x%（无变化）。
        List<String> descriptions = new ArrayList<>();
        if (!CollectionUtils.isEmpty(usages)) {
            Map<Integer, Map<Integer, TeacherIdentityResourceUsagePO>> group = usages.stream().collect(Collectors.groupingBy(CommonDimensionEntity::getDateCode,
                                                                                                                             Collectors.toMap(TeacherIdentityResourceUsagePO::getRoleIdentity, Function.identity())));
            Map<Integer, TeacherIdentityResourceUsagePO> current = group.get(dateCode);
            descriptions.add(Optional.ofNullable(current)
                                     .map(m -> {
                                         TeacherIdentityResourceUsagePO po = m.get(0);
                                         return Optional.ofNullable(po)
                                                 .map(usage -> "学校共计有" + po.getTeacherNumber() + "位学科教师使用了资源，使用资源的学科教师占比为" + NumberFormatUtil.formatPercent(po.getTeacherRate()))
                                                 .map(s -> {
                                                     // 较上月变化
                                                     Map<Integer, TeacherIdentityResourceUsagePO> last = group.get(lastMonthDateCode);
                                                     if (!CollectionUtils.isEmpty(last)) {
                                                         TeacherIdentityResourceUsagePO lastPo = last.get(0);
                                                         if (Objects.isNull(lastPo)) {
                                                             return s;
                                                         }
                                                         Double lastNumber = Optional.of(lastPo)
                                                                 .map(TeacherIdentityResourceUsagePO::getTeacherRate)
                                                                 .orElse(null);
                                                         if (Objects.isNull(lastNumber)) {
                                                             return s;
                                                         } else {
                                                             double num = po.getTeacherRate() - (lastNumber);
                                                             return s + "，较上月" + (num == 0 ? "无变化" : ((num > 0 ? "增加" : "减少") + NumberFormatUtil.formatPercent(Math.abs(num))));
                                                         }
                                                     } else {
                                                         return s;
                                                     }
                                                 })
                                                 .orElse("学校无使用数据");
                                     })
                                     .orElse("学校无使用数据"));
            descriptions.add(Description.getDescription(cooperativeTeachers.stream().findFirst().orElse(null),
                                                        !CollectionUtils.isEmpty(secretExamReceives),
                                                        Objects.isNull(current) ? null : current.get(1),
                                                        Objects.isNull(group.get(lastMonthDateCode)) ? null : group.get(lastMonthDateCode).get(1)));
        } else {
            descriptions.add("学校无使用数据");
        }

        // 教师资源应用情况
        map.put("teacher_resource_utilization", descriptions.stream().filter(StringUtils::isNotBlank).collect(Collectors.joining("；", "本月，", "。")));

        Map<Integer, Map<Integer, TeacherIdentityResourceUsagePO>> groupByRole = usages.stream().collect(Collectors.groupingBy(TeacherIdentityResourceUsagePO::getRoleIdentity,
                                                                                                                               Collectors.toMap(TeacherIdentityResourceUsagePO::getDateCode, Function.identity())));

        // 教师资源应用情况表
        List<TeacherResourceUtilizationReportData> list = new ArrayList<>();
        Map<Integer, TeacherIdentityResourceUsagePO> teacher = groupByRole.get(0);
        if (!CollectionUtils.isEmpty(teacher)) {
            fillTableData(teacher, lastMonthDateCode, dateCode, list, "学科教师");
        }

        Map<Integer, TeacherIdentityResourceUsagePO> secretPeople = groupByRole.get(1);
        if (!CollectionUtils.isEmpty(secretPeople)) {
            fillTableData(secretPeople, lastMonthDateCode, dateCode, list, "试卷保密员");
        }
        map.put("teacher_resource_utilization_table", list);

        return map;
    }

    private static void fillTableData(Map<Integer, TeacherIdentityResourceUsagePO> teacher, Integer lastMonthDateCode, Integer dateCode, List<TeacherResourceUtilizationReportData> list, String role) {
        TeacherIdentityResourceUsagePO lastMonth = teacher.get(lastMonthDateCode);
        TeacherIdentityResourceUsagePO currentMonth = teacher.get(dateCode);

        boolean b = Objects.isNull(lastMonth) || Objects.isNull(currentMonth);
        String change = "-";
        if (!b) {
            BigDecimal num = BigDecimal.valueOf(currentMonth.getTeacherRate()).subtract(BigDecimal.valueOf(lastMonth.getTeacherRate()));
            change = (num.compareTo(BigDecimal.ZERO) == 0 ? "无变化" : ((num.compareTo(BigDecimal.ZERO) > 0 ? "+" : "-") + NumberFormatUtil.formatPercent(Math.abs(num.doubleValue()))));
        }
        list.add(TeacherResourceUtilizationReportData.builder()
                         .role(role)
                         .lastMonthUseNumber(Objects.isNull(lastMonth) ? "-" : String.valueOf(lastMonth.getTeacherNumber()))
                         .currentMonthUseNumber(Objects.isNull(currentMonth) ? "-" : String.valueOf(currentMonth.getTeacherNumber()))
                         .lastMonthUseRage(Objects.isNull(lastMonth) ? "-" : NumberFormatUtil.formatPercent(lastMonth.getTeacherRate(), "-"))
                         .currentMonthUseRage(Objects.isNull(currentMonth) ? "-" : NumberFormatUtil.formatPercent(currentMonth.getTeacherRate()))
                         .change(change)
                         .build());
    }


    /**
     * The enum Description.
     */
    enum Description {
        /**
         * 当月合作了试卷保密员，且未接收到绝密资料
         */
        LEVEL1((cooperativeTeacher, receivedSecretExam, current, last) ->
                       Objects.nonNull(cooperativeTeacher)
                               && !receivedSecretExam
                               && cooperativeTeacher.getResourceSecretTeacherNumber() > 0,
               (current, last) -> "本月暂未接收到绝密试卷"),
        /**
         * 当月合作了试卷保密员，且接收到绝密资料，且合作了1位试卷保密员, 使用资源
         */
        LEVEL2((cooperativeTeacher, receivedSecretExam, current, last) ->
                       Objects.nonNull(cooperativeTeacher)
                               && receivedSecretExam
                               && cooperativeTeacher.getResourceSecretTeacherNumber() == 1
                               && Objects.nonNull(current) && current.getTeacherNumber() > 0,
               (current, last) -> "试卷保密员使用了资源"),
        /**
         * 当月合作了试卷保密员，且接收到绝密资料，且合作了1位试卷保密员，未使用资源
         */
        LEVEL3((cooperativeTeacher, receivedSecretExam, current, last) ->
                       Objects.nonNull(cooperativeTeacher)
                               && receivedSecretExam
                               && cooperativeTeacher.getResourceSecretTeacherNumber() == 1
                               && (Objects.isNull(current) || current.getTeacherNumber() == 0),
               (current, last) -> "试卷保密员未使用资源"),
        /**
         * 当月该学校合作了2位及以上的试卷保密员,使用了资源
         */
        LEVEL4((cooperativeTeacher, receivedSecretExam, current, last) ->
                       Objects.nonNull(cooperativeTeacher)
                               && receivedSecretExam
                               && cooperativeTeacher.getResourceSecretTeacherNumber() > 1
                               && Objects.nonNull(current) && current.getTeacherNumber() > 0,
               (current, last) -> {
                   String desc = current.getTeacherNumber() + "位试卷保密员使用了资源，使用资源的试卷保密员占比为" + NumberFormatUtil.formatPercent(current.getTeacherRate());

                   if (!Objects.isNull(last)) {
                       Double lastNumber = Optional.of(last)
                               .map(TeacherIdentityResourceUsagePO::getTeacherRate)
                               .orElse(null);
                       if (Objects.isNull(lastNumber)) {
                           return desc;
                       }
                       double num = current.getTeacherRate() - (lastNumber);
                       desc += "，使用资源的试卷保密员占比较上月" + (num == 0 ? "无变化" : ((num > 0 ? "增加" : "减少") + NumberFormatUtil.formatPercent(Math.abs(num))));
                   }
                   return desc;
               }
        ),
        /**
         * 当月该学校合作了2位及以上的试卷保密员, 未使用资源
         */
        LEVEL5((cooperativeTeacher, receivedSecretExam, current, last) ->
                       Objects.nonNull(cooperativeTeacher)
                               && receivedSecretExam
                               && cooperativeTeacher.getResourceSecretTeacherNumber() > 1
                               && (Objects.isNull(current) || current.getTeacherNumber() == 0),
               (current, last) -> {
                   String desc = "试卷保密员未使用资源";
                   if (Objects.isNull(current) || Objects.isNull(last)) {
                       return desc;
                   }
                   Double currentNumber = Optional.of(current)
                           .map(TeacherIdentityResourceUsagePO::getTeacherRate)
                           .orElse(null);

                   Double lastNumber = Optional.of(last)
                           .map(TeacherIdentityResourceUsagePO::getTeacherRate)
                           .orElse(null);

                   if (Objects.isNull(currentNumber) || Objects.isNull(lastNumber)) {
                       return desc;
                   }

                   double num = currentNumber - (lastNumber);
                   desc += "，使用资源的试卷保密员占比较上月" + (num == 0 ? "无变化" : ((num > 0 ? "增加" : "减少") + NumberFormatUtil.formatPercent(Math.abs(num))));
                   return desc;
               }
        ),
        ;
        private final Predicate predicate;
        private final BiFunction<TeacherIdentityResourceUsagePO, TeacherIdentityResourceUsagePO, String> function;

        Description(Predicate predicate, BiFunction<TeacherIdentityResourceUsagePO, TeacherIdentityResourceUsagePO, String> function) {
            this.predicate = predicate;
            this.function = function;
        }

        /**
         * Gets description.
         *
         * @param cooperativeTeacher the cooperative teacher
         * @param receivedSecretExam the received secret exam
         * @param current            the current
         * @param last               the last
         * @return the description
         */
        public static String getDescription(CooperativeTeacherPO cooperativeTeacher,
                                            boolean receivedSecretExam,
                                            TeacherIdentityResourceUsagePO current,
                                            TeacherIdentityResourceUsagePO last) {
            return Arrays.stream(values())
                    .filter(d -> d.predicate.test(cooperativeTeacher, receivedSecretExam, current, last))
                    .findFirst()
                    .map(d -> d.function.apply(current, last))
                    .orElse("");
        }
    }

    /**
     * The interface Predicate.
     */
    interface Predicate {
        /**
         * Test boolean.
         *
         * @param cooperativeTeacher 合作教师
         * @param receivedSecretExam 是否接收到绝密试卷
         * @param current            当月使用情况
         * @param last               上月使用情况
         * @return the boolean
         */
        boolean test(CooperativeTeacherPO cooperativeTeacher,
                     boolean receivedSecretExam,
                     TeacherIdentityResourceUsagePO current,
                     TeacherIdentityResourceUsagePO last);
    }
}
